#ifndef __RENDER_TARGET_H__
#define __RENDER_TARGET_H__

#pragma once

#include "Camera.h"
#include "Texture.h"

namespace Nezha
{

#define NEZHA_MAX_MULTIPLE_RENDER_TARGETS 8

	class RenderTarget;

	/** simply viewport data structure.
	@REMARKS
		view port rect's component should be normalized to [0, 1]
	*/
	class NEZHA_ENGINE_ENTRY Viewport
	{
		friend class RenderTarget;
	public:
		Viewport()
			:mActive(true)
			,mPriority(0)
			,mClearFlag(FBO_COLOR | FBO_DEPTH | FBO_STENCIL)
			,mRenderLayerMask(RL_ALL)
			,mBackColor(RGBA::BLACK)
			,mDepthVal(1.0f)
			,mTarget(NULL)
		{
			mViewportRect.RightCorner = Tuple2<int>(1, 1);
		}

		virtual ~Viewport(){}

		bool mActive;
		u8 mClearFlag;
		u32 mRenderLayerMask;
		RGBA mBackColor;
		float mDepthVal;

		RenderTarget* getRenderTarget()
		{
			return mTarget;
		}

		int getPriority() const
		{
			return mPriority;
		}

		const RectI& getRect() const
		{
			return mViewportRect;
		}

		void setViewportRect(const RectI& rc)
		{
			mViewportRect = rc;
		}

	private:
		int mPriority;
		RectI mViewportRect;
		RenderTarget* mTarget;
	}; 

	class NEZHA_ENGINE_ENTRY RenderTargetListener
	{
	public:
		virtual ~RenderTargetListener() {}

		virtual void onPreDraw(Viewport* vp) = 0;

		virtual void onPostDraw(Viewport* vp) = 0;
	};

	class RenderTarget;

	/** depth-stencil buffer, can be shared with multiple render targets. */
	class NEZHA_ENGINE_ENTRY DepthStencilBuffer : public Refable
	{
	public:
		virtual ~DepthStencilBuffer(){}

		DepthStencilBuffer(u8 bitDepth, u16 w, u16 h);

		int getHandle() const
		{ 
			return mHandle;
		}

		u16 getWidth() const
		{
			return mWidth;
		}

		u16 getHeight() const
		{
			return mHeight;
		}

		u8 getBitDepth() const
		{
			return mBitDepth;
		}

		virtual bool isCompatible(RenderTarget* rt) const;
		
	protected:
		int mHandle;
		u16 mWidth;
		u16 mHeight;
		u8 mBitDepth;
	};

	typedef RefCountedPtr<DepthStencilBuffer>::Default DepthStencilBufferPtr;

	class NEZHA_ENGINE_ENTRY RenderTarget
	{
	public:
		RenderTarget();
		virtual ~RenderTarget();

		void setCamera(Camera* cam);

		/**
		@return
			return NULL if subRect is not valid or the specific priority was engaged.
		*/
		virtual Viewport* addViewport(int priority, const RectI& subRect);

		Viewport* getViewport(u32 i);

		Viewport* getViewportPriority(int priority);

		u32 getNumViewports() const
		{
			return mViewportList.size();
		}

		/**
		@REMARKS
			multiple viewports might have same priority.
		*/
		virtual void removeViewport(int priority);

		virtual void removeAllViewports();

		void resetViewportPriority(Viewport* vp, int p);

		virtual bool attachDepthStencilBuffer(DepthStencilBuffer* ds);

		virtual void detachDepthStencilBuffer();

		virtual void swapBuffers(bool waitVSync = true)
		{
			(void)waitVSync;
		}

		void addListener(RenderTargetListener* rtl);

		void removeListener(RenderTargetListener* rtl);

		void removeAllListeners();

		void setPriority(int priority)
		{
			mPriority = priority;
		}

		int getPriority() const
		{
			return mPriority;
		}

		u32 getVisiblityMask() const
		{
			return mVisiblityMask;
		}

		void setVisiblityMask(u32 msk);

		//TexFormat getColorBufferFormat() const
		//{
		//	
		//}

		u16 getWidth() const
		{
			return mWidth;
		}

		u16 getHeight() const
		{
			return mHeight;
		}

		u16 getColorDepth() const
		{
			return mColorDepth;
		}

		bool hasDepthStencil() const
		{
			return mDepthStencilBuffer.ptr() != NULL;
		}

		/** retrieve render texture if have. */
		virtual Texture* getRenderTexture(u32 index) = 0;

		DepthStencilBuffer* getDepthStencilBuffer()
		{
			return mDepthStencilBuffer.get();
		}

		Camera* getCamera()
		{
			return mCamera;
		}

NEZHA_INTERNAL:
		void preDraw(Viewport* vp);

		void postDraw(Viewport* vp);

	protected:
		virtual void _preDraw(Viewport* vp) {}

		virtual void _postDraw(Viewport* vp) {}

		typedef PArray<RenderTargetListener*> RTListenerList;
		typedef PArray<Viewport*> ViewportList;

		// FSAA ?

		u32 mVisiblityMask;

		RTListenerList mListeners;
		ViewportList mViewportList;

		Camera* mCamera;
		int mPriority;

		DepthStencilBufferPtr mDepthStencilBuffer;
		
		// targets info
		u16 mWidth;
		u16 mHeight;
		u16 mColorDepth;
	};


	class NEZHA_ENGINE_ENTRY RTTManager : public RefCountedResManager
	{
	public:
		virtual ~RTTManager(){}

		virtual RenderTarget* createRenderTarget(u16 w, u16 h, u16 aa, TexFormat fmt, bool renderTexture = false) = 0;

		virtual void destroyRenderTarget(RenderTarget* rt) = 0;

		virtual void setCurrent(RenderTarget* rt) = 0;

		virtual DepthStencilBufferPtr createDepthStencilBuffer(u16 w, u16 h, u16 aa, u8 bit) = 0;

		virtual void destroyAll() = 0;
	};

}//end namespace Nezha

#endif //end __RENDER_TARGET_H__